#pragma once

#include <map>
#include <unordered_map>
#include <unordered_set>
#include <set>
#include <string>
#include <vector>
#include <deque>
#include <fstream>
#include <sstream>
#include <turbo/container/small_vector.h>

namespace std {
    template<typename T>
    std::ostream &operator <<(std::ostream &os, const std::vector<T> &v) {
        if (v.empty()) {
            return os << "[]";
        }
        os << "[" << v[0];
        for (size_t i = 1; i < v.size(); i++) {
            os << ", " << v[i];
        }
        os << "]";
        return os;
    }

    template<>
    inline std::ostream &operator <<(std::ostream &os, const std::vector<std::string> &v) {
        if (v.empty()) {
            return os << "[]";
        }
        os << "[\"" << v[0];
        for (size_t i = 1; i < v.size(); i++) {
            os << "\", \"" << v[i];
        }
        os << "\"]";
        return os;
    }

    template<typename T>
    std::ostream &operator <<(std::ostream &os, const deque<T> &dq) {
        if (dq.empty()) {
            return os << "[]";
        }
        os << "[\"" << dq[0];
        for (size_t i = 1; i < dq.size(); i++) {
            os << "\", \"" << dq[i];
        }
        os << "\"]";
        return os;
    }


    template<class T1, class T2>
    std::ostream &operator <<(std::ostream &os, const pair<T1, T2> &pr) {
        os << pr.first << ":" << pr.second;
        return os;
    }


    template<class T>
    std::string &operator <<(std::string &str, const T &obj) {
        std::stringstream ss;
        ss << obj; // call std::ostream& operator << (std::ostream& os,
        return str = ss.str();
    }

    template<class T1, class T2>
    std::ostream &operator <<(std::ostream &os, const map<T1, T2> &mp) {
        if (mp.empty()) {
            os << "{}";
            return os;
        }
        os << '{';
        typename map<T1, T2>::const_iterator it = mp.begin();
        os << *it;
        it++;
        while (it != mp.end()) {
            os << ", " << *it;
            it++;
        }
        os << '}';
        return os;
    }

    template<class T1, class T2>
    std::ostream &operator <<(std::ostream &os, const std::unordered_map<T1, T2> &mp) {
        if (mp.empty()) {
            return os << "{}";
        }
        os << '{';
        typename std::unordered_map<T1, T2>::const_iterator it = mp.begin();
        os << *it;
        it++;
        while (it != mp.end()) {
            os << ", " << *it++;
        }
        return os << '}';
    }

    template<class T>
    std::ostream &operator <<(std::ostream &os, const set<T> &st) {
        if (st.empty()) {
            os << "{}";
            return os;
        }
        os << '{';
        typename set<T>::const_iterator it = st.begin();
        os << *it;
        it++;
        while (it != st.end()) {
            os << ", " << *it;
            it++;
        }
        os << '}';
        return os;
    }

    template<class KeyType, class ContainType>
    bool IsIn(const ContainType &contain, const KeyType &key) {
        return contain.end() != contain.find(key);
    }

    template<class T>
    basic_string<T> &operator <<(basic_string<T> &s, ifstream &ifs) {
        return s.assign((istreambuf_iterator<T>(ifs)), istreambuf_iterator<T>());
    }

    template<class T>
    std::ofstream &operator <<(std::ofstream &ofs, const basic_string<T> &s) {
        ostreambuf_iterator<T> itr(ofs);
        copy(s.begin(), s.end(), itr);
        return ofs;
    }

    template <class T>
    std::ostream & operator << (std::ostream& os, const turbo::SmallVector<T>& vec) {
        if(vec.empty()) {
            return os << "[]";
        }
        os<<"[\""<<vec[0];
        for(size_t i = 1; i < vec.size(); i++) {
            os<<"\", \""<<vec[i];
        }
        os<<"\"]";
        return os;
    }
} // namespace std
